﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using PMC.Interfaces;
using PMC.Plugins;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Net;
using PMC.Scripts;
using PMC.Structs;
using PMC.Urls;
using mshtml;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;
using Timer = System.Windows.Forms.Timer;

namespace PMC
{
    /// <summary>
    /// Old WebBrowser that wat built upon the WebBrowser control, now unsupported because of a lot of minor bugs that this control has.
    /// Also the rendering of images, CSS and JS were overrated
    /// </summary>
    [Obsolete("Not supported anymore. Please use WBrowserWebRequest")]
    public sealed class Wbrowser : WebBrowser, IWbrowser
    {
        private Timer UrlTimer = new Timer();
        private Timer safetyTimer = new Timer();

        /// <summary>
        /// Interval to wait before navigating to next webpage
        /// </summary>
        public int TimerInterval
        {
            get
            {
                return UrlTimer.Interval;
            }
            set
            {
                if (value <= 0)
                    throw new ArgumentException("Interval must be greater than 0");
                UrlTimer.Interval = value;
                safetyTimer.Interval = value + 200;
            }
        }

        private DateTime _timerElapse = DateTime.Now;
        /// <summary>
        /// Date of the last time the timer elapsed
        /// </summary>
        public DateTime TimerElapse
        {
            get { return _timerElapse; }
        }

        private Website currentWebsite;
        /// <summary>
        /// Current website if found, otherwise <b>null</b>
        /// </summary>
        /// <remarks>
        /// Since this property can return <b>null</b> please don't forget checking. Otherwise a <b>NullReference</b> exception may be thrown.
        /// </remarks>
        public Website CurrentWebsite
        {
            get
            {
                return currentWebsite;
            }
        }

        /// <summary>
        /// Boolean representing if sounds of the webbrowser should be enabled.
        /// </summary>
        public static bool IsSoundEnabled
        {
            get { return NativeMethods.IsSoundEnabled; }
            set
            {
                if (value != Properties.Settings.Default.SoundEnabled)
                    Properties.Settings.Default.SoundEnabled = value;
                NativeMethods.IsSoundEnabled = value;
            }
        }

        private BrowserState currentState = BrowserState.Stopped;
        /// <summary>
        /// The current state the browser-control is in
        /// </summary>
        public BrowserState State
        {
            get { return currentState; }
            private set { currentState = value; OnBrowserStateChanged(); }
        }

        public new HtmlDocument Document
        {
            get
            {
                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(DocumentText);
                return doc;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public Wbrowser()
        {
            IsWebBrowserContextMenuEnabled = false;
            this.DocumentCompleted += Wbrowser_DocumentCompleted;
            this.Navigated += Wbrowser_Navigated;
            this.ScriptErrorsSuppressed = true;
            UrlTimer.Interval = 10000;
            safetyTimer.Interval = 10000;
            UrlTimer.Tick += UrlTimer_Tick;
            safetyTimer.Tick += safetyTimer_Tick;
            NewWindow += Wbrowser_NewWindow;
            Log.Instance.AddMessage("New browser [" + GetHashCode() + "] sucssesfully added!", LogType.Warning);
            IsSoundEnabled = !Properties.Settings.Default.SoundEnabled;
            State = BrowserState.Stopped;
        }

        void UrlList_UrlAdded(object sender, UrlEventArgs e)
        {
            if (State == BrowserState.Stopped)
            {
                Invoke((MethodInvoker) Start);
                UriCollector.UrlAdded -= UrlList_UrlAdded;
            }
        }

        void safetyTimer_Tick(object sender, EventArgs e)
        {
            if (ReadyState != WebBrowserReadyState.Complete)
            {
                base.Stop();
                OnDocumentCompleted(new WebBrowserDocumentCompletedEventArgs(Url));
            }
            safetyTimer.Stop();
        }

        /// <summary>
        /// New window eventhandler, used for blocking pop-ups!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Wbrowser_NewWindow(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Log.Instance.AddMessage("Pop-up succesfully blocked", LogType.Warning);
            e.Cancel = true;
        }

        /// <summary>
        /// Some fixes for the browser
        /// <br/>Credits to : http://stackoverflow.com/questions/77659/blocking-dialogs-in-net-webbrowser-control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Wbrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            try
            {
                if (base.Document != null)
                {
                    var head = base.Document.GetElementsByTagName("head")[0];
                    var scriptEl = base.Document.CreateElement("script");
                    var element = (IHTMLScriptElement)scriptEl.DomElement;
                    const string alertBlocker = "window.focus=function() {}; window.alert=function() {}; window.confirm=function() {}; window.onerror = function (e) { return true; }; window.onbeforeunload = null;";
                    element.text = alertBlocker;
                    head.AppendChild(scriptEl);
                }
            }
            catch (Exception exception)
            {
                Log.Instance.AddMessage(exception);
            }
        }

        /// <summary>
        /// Webpage was loaded, handles the webpage
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Wbrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            //Check if iframe
            Uri Url = e.Url;
            if (!Url.Equals(this.Url) || Url == null)
                return;

            OnWebpageVisited();
            Log.Instance.AddMessage("Navigated: " + Url);

            currentWebsite = Website.WebsiteByUrl(Url);
            var currentScript = CurrentWebsite == null ? Script.Default : CurrentWebsite.Script;
            var handle = currentScript.Handle(this);
            switch (handle.Handler)
            {
                    case DocumentHandler.Wait:
                        TimerInterval = currentScript.Interval;
                        UrlTimer.Start();
                    break;
                    case DocumentHandler.Skip:
                        Next();
                    break;
                    case DocumentHandler.Reload:
                        Refresh();
                    break;
                    case DocumentHandler.Replace:
                        Navigate(Convert.ToString(handle.Argument0));
                    break;
                    case DocumentHandler.Post:
                        var bytes = Encoding.UTF8.GetBytes(handle.Argument0.ToString());
                        Navigate(Url, "", bytes, Convert.ToString(handle.Argument1));
                    break;
                    case DocumentHandler.NoAction:
                    break;
            }
            _timerElapse = DateTime.Now;
        }

        /// <summary>
        /// Timer interval was reached, raises Next-method and stops the timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void UrlTimer_Tick(object sender, EventArgs e)
        {
            UrlTimer.Stop();
            Next();
        }

        /// <summary>
        /// Navigates to the next url in the list
        /// Throws:
        /// ConnectionException | No internetconnection available
        /// InvalidOperatoinException | Empty queue
        /// </summary>
        public void Next()
        {
            if (!Global.CheckInternetConnection())
            {
                Log.Instance.AddException(new ConnectionException());
                Stop();
                return;
            }

            //CheckSafe();

            if (UriCollector.ActiveCollector.Count > 0)
            {
                var url = UriCollector.ActiveCollector.Next();
                var cookie = NativeMethods.GetCookieInternal(url, false);
                if (cookie.Trim() != string.Empty)
                    Navigate(url, "", null, "Cookie: " + NativeMethods.GetCookieInternal(url, false) + Environment.NewLine);
                else
                    Navigate(url);
                safetyTimer.Start();
            }
            else
            {
                Stop();
            }
        }

        public Task NextAsync()
        {
            throw new NotImplementedException();
        }

        private void CheckSafe()
        {
            if (ReadyState == WebBrowserReadyState.Uninitialized)
                return;

            while (ReadyState != WebBrowserReadyState.Complete)
            {
                if (IsDisposed)
                    break;
                Application.DoEvents();
            }
        }

        public void Start()
        {
            Log.Instance.AddMessage("Webbrowser [" + GetHashCode() + "] activated");
            State = BrowserState.Started;
            Next();
        }

        public Task StartAsync()
        {
            throw new NotImplementedException();
        }

        public new void Stop()
        {
            Log.Instance.AddMessage("Webbrowser [" + GetHashCode() + "] deactivated");
            State = BrowserState.Stopped;
            UrlTimer.Stop();
            UriCollector.UrlAdded += UrlList_UrlAdded;
        }

        public void SetCookie(Uri url, CookieCollection cookie)
        {
            NativeMethods.SetCookiesInternal(cookie, url);
        }

        public CookieCollection GetCookies(Uri url)
        {
            return NativeMethods.GetCookieInternal(url);
        }

        public CookieCollection GetCookies()
        {
            return NativeMethods.GetCookieInternal(Url);
        }

        public event webpageChanged WebpageVisited;
        public void OnWebpageVisited()
        {
            if (WebpageVisited != null)
            {
                WebpageVisited(CurrentWebsite, Url);
            }
        }

        public event browserStateChanged BrowserStateChanged;
        public void OnBrowserStateChanged()
        {
            if (BrowserStateChanged != null)
            {
                BrowserStateChanged(currentState);
            }
        }
    }
}
